package br.com.android.persistence;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import br.com.android.interfaces.Column;
import br.com.android.interfaces.PrimaryKey;
import br.com.android.interfaces.Table;

public abstract class BaseEntity<T> {

	private static StringBuilder DQL;
	private static List<String> columnsFields;
	private static List<String> columns;
	private static List<String> fieldColumns;
	private static String PK = "";
	private SQLiteDatabase dataBase;
	private static Field[] fields;
	private static Class<?> object;
	private static String tableName;

	public BaseEntity(Class<T> model, SQLiteDatabase database) {
		try {
			this.dataBase = database;
			object = Class.forName(model.getName());
			createStatement();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void createStatement() throws Exception {
		if (object.isAnnotationPresent(Table.class)) {
			Annotation annotation = object.getAnnotation(Table.class);
			Method method = annotation.getClass().getMethod("name");
			Object object = method.invoke(annotation);
			tableName = object.toString();
			DQL = new StringBuilder();
		} else {
			throw new Exception();
		}

		fieldColumns = new ArrayList<String>();
		columns = new ArrayList<String>();
		columnsFields = new ArrayList<String>();
		fields = object.getDeclaredFields();

		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			Annotation annotation = null;
			Method methodName = null;
			Object objectName = null;
			String type = "";
			String primaryKeyText = "";
			String notNull = "";
			String unique = "";

			if (field.isAnnotationPresent(Column.class)) {
				annotation = field.getAnnotation(Column.class);
				methodName = annotation.getClass().getMethod("name");
				objectName = methodName.invoke(annotation);

				Method notNullMethod = annotation.getClass().getMethod(
						"notNull");
				Object objectNotNull = notNullMethod.invoke(annotation);
				if (Boolean.valueOf(objectNotNull.toString())) {
					notNull += " NOT NULL";
				} else
					notNull += " NULL";

				Method uniqueMethod = annotation.getClass().getMethod("unique");
				Object objectUnique = uniqueMethod.invoke(annotation);
				if (Boolean.valueOf(objectUnique.toString()))
					unique += " UNIQUE";

				if (field.isAnnotationPresent(PrimaryKey.class)) {
					PK = objectName.toString();

					Annotation pKey_annotation = field
							.getAnnotation(PrimaryKey.class);
					Method pkey_methodAutoIncrement = pKey_annotation
							.getClass().getMethod("autoIncrement");
					Object pkey_autoIncrement = pkey_methodAutoIncrement
							.invoke(pKey_annotation);

					primaryKeyText = " PRIMARY KEY";

					if (Boolean.valueOf(pkey_autoIncrement.toString())) {
						primaryKeyText += " AUTOINCREMENT";
					}
				}

				if (field.getType() == int.class
						|| field.getType() == Integer.class
						|| field.getType() == Long.class
						|| field.getType() == long.class) {
					type = " INTEGER";
				} else if (field.getType() == String.class
						|| field.getType() == char.class) {
					type = " TEXT";
				} else if (field.getType() == Double.class
						|| field.getType() == Float.class
						|| field.getType() == double.class
						|| field.getType() == float.class) {
					type = " REAL";
				} else if (field.getType() == BigDecimal.class) {
					type = " NUMERIC";
				} else if (field.getType() == Date.class) {
					type = " TIMESTAMP";
				} else if (field.getType() == Boolean.class
						|| field.getType() == boolean.class) {
					type = " BOOLEAN";
				}

				if (primaryKeyText.equals(""))
					columnsFields.add(objectName + type + notNull + unique);
				else
					columnsFields.add(PK + type + primaryKeyText);
				columns.add(objectName.toString());
				fieldColumns.add(type);
			} else {
				throw new Exception();
			}
		}
		DQL.append(" CREATE TABLE " + tableName + " ( ");
		if (PK.equals(""))
			DQL.append("ID INTEGER PRIMARY KEY AUTOINCREMNET ");
		DQL.append(columnsFields.toString().replace("[", "").replace("]", ""));
		DQL.append("); ");
		Log.v("PERSISTENCE", DQL.toString());
		Log.v("PERSISTENCE", columns.toString());
	}

	public void onCreate() throws Exception {
		if (columns != null) {
			dataBase.execSQL(DQL.toString());
		} else {
			throw new Exception("Table not created, the Create DDL not found");
		}
	}

	/** Upgrade (if necessary) the table */
	public void onUpgrade(int oldVersion,
			int newVersion) {
		try {
			dataBase.execSQL("DROP TABLE IF EXISTS " + tableName);
			onCreate();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static String getPK() {
		return PK;
	}

	public static Field[] getFieldDefinition() {
		return fields;
	}

	public static StringBuilder getDQL() {
		return DQL;
	}

	public static List<String> getColumns() {
		return columns;
	}

	public static List<String> getFieldColumns() {
		return fieldColumns;
	}

	public static String getTableName() {
		return tableName;
	}

	public long save(T obj) throws Exception {
		long result = 0;
		ContentValues values = new ContentValues();
		for (int i = 0; i < columns.size(); i++)
			for (int e = 0; e < object.getDeclaredMethods().length; e++) {
				Method method = object.getDeclaredMethods()[e];
				if (method.getName().equalsIgnoreCase("get" + columns.get(i))) {
					Log.v("PERSISTENCE", columns.get(i));
					Log.v("PERSISTENCE", method.invoke(obj).toString());
					values.put(columns.get(i), method.invoke(obj).toString());
					break;
				}
			}
		result = dataBase.insert(getTableName(), null, values);
		return result;
	}
}
